package com.atguigu.gulimail.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.constant.OrderStatusEnum;
import com.atguigu.common.to.OrderTo;
import com.atguigu.common.to.SkuStock;
import com.atguigu.common.to.wareTo.StockDetailTo;
import com.atguigu.common.to.wareTo.StockLockedTo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimail.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimail.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimail.ware.exceptio.NoStockException;
import com.atguigu.gulimail.ware.feign.MemberFeignService;
import com.atguigu.gulimail.ware.feign.OrderFeignService;
import com.atguigu.gulimail.ware.feign.ProductFeignService;
import com.atguigu.gulimail.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimail.ware.service.WareOrderTaskService;
import com.atguigu.gulimail.ware.vo.*;
import com.rabbitmq.client.Channel;
import lombok.Data;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimail.ware.dao.WareSkuDao;
import com.atguigu.gulimail.ware.entity.WareSkuEntity;
import com.atguigu.gulimail.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    WareOrderTaskService orderTaskService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    WareOrderTaskDetailService detailService;

    @Autowired
    OrderFeignService orderFeignService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {

        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();

        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)) {
            queryWrapper.eq("ware_id", wareId);
        }
        String skuId = (String) params.get("skuId");
        if (!StringUtils.isEmpty(skuId)) {
            queryWrapper.eq("sku_id", skuId);
        }

        //   wareId: 123,//仓库id
        //   skuId: 123//商品id
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void updateWareNum(Long skuId, Integer skuNum, Long wareId) {
        List<WareSkuEntity> wareSkuEntities = this.baseMapper.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (wareSkuEntities == null || wareSkuEntities.size() == 0) {
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            //TODO 还有什么办法可以让异常出现了不回滚？
            try {
                R info = productFeignService.info(skuId);
                if (info.getCode() == 0) {
                    Map<String, Object> map = (Map<String, Object>) info.get("info");
                    wareSkuEntity.setSkuName((String) map.get("skuName"));
                }
            } catch (Exception e) {

            }


            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setWareId(wareId);
            this.baseMapper.insert(wareSkuEntity);
        }
        //更新的前提的 数据库保存了当前商品的信息。 如果没有保存 那就插入一条数据
        this.baseMapper.updateWareNum(skuId, skuNum, wareId);
    }

    @Override
    public List<SkuStock> hasStock(List<Long> skuIds) {
        List<SkuStock> Skustocks = skuIds.stream().map(skuId -> {
            SkuStock skuStock = new SkuStock();
            Long stock = this.baseMapper.getSkuStockBySkuId(skuId);
            skuStock.setSkuId(skuId);
            skuStock.setHasStock(stock == null ? false : stock > 0);
            return skuStock;
        }).collect(Collectors.toList());

        return Skustocks;
    }

    @Override
    public FareAddressVo getFare(Long addrId) {
        //
        FareAddressVo fareAddressVo=new FareAddressVo();
        R r = memberFeignService.addrInfo(addrId);
        MemberAddressVo memberReceiveAddress = r.getData("memberReceiveAddress", new TypeReference<MemberAddressVo>() {
        });
        fareAddressVo.setMemberAddressVo(memberReceiveAddress);
        String phone = memberReceiveAddress.getPhone();
        String substring = phone.substring(phone.length() - 1, phone.length());

        BigDecimal fare = new BigDecimal(substring);
        fareAddressVo.setFare(fare);
        return fareAddressVo;

    }

    /**
     * 库存解锁
     *  1、订单下单成功。库存锁定成功，后面积分服务失败，导致订单回滚。那么之前锁定的库存就要解锁
     *  2、订单下单成功，锁定库存失败。
     *
     */

//
//    public void handleStockLockedRelease(Message message, StockLockedTo to, Channel channel) throws IOException {
//        System.out.println("库存解锁");
//        StockDetailTo stockDetailTo = to.getStockDetailTo();
//        Long orderTaskID = to.getOrderTaskID();
//        if (orderTaskID!=null){
//            //不为null  才说明库存锁定了
//            WareOrderTaskEntity byId = orderTaskService.getById(orderTaskID);
//            String orderSn = byId.getOrderSn();
//            //通过订单号获取订单。
//            R r = orderFeignService.getOrder(orderSn);
//            if (r.getCode()==0){
//                OrderVo data = r.getData(new TypeReference<OrderVo>() {
//                });
//                if (data==null||data.getStatus()== OrderStatusEnum.CANCLED.getCode()){
//                    //如果订单为null 或者订单状态已经关闭
//                        //订单数据回滚。
//                    if (stockDetailTo.getLockStatus()==1){
//
//                        unlockStock(stockDetailTo.getSkuId(),stockDetailTo.getWareId(),stockDetailTo.getSkuNum(),stockDetailTo.getId());
//                    }
//                }
//            }
//        }
//
//
//    }

    private void unlockStock(Long skuId, Long wareId, Integer skuNum,Long detailId) {
        this.baseMapper.unlockStock(skuId,wareId,skuNum,detailId);
        //修改detailTask状态
        WareOrderTaskDetailEntity entity = new WareOrderTaskDetailEntity();
        entity.setId(detailId);
        entity.setLockStatus(2);
        detailService.updateById(entity);
    }


    /**
     * 为某个订单锁定库存
     * 这是一个事务 只要有一个订单项没有库存。则直接回滚
     *
     *
     * @param lockVo
     * @return
     */
    @Transactional
    @Override
    public Boolean lockStock(WareLockVo lockVo) {
        //锁定库存时，有两个表来记录库存的锁定
        //`wms_ware_order_task`  主要来存放订单号。
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        //订单创建成功了 就一定会有订单号。
        wareOrderTaskEntity.setOrderSn(lockVo.getOrderSn());
        orderTaskService.save(wareOrderTaskEntity);

        //锁定库存。
        //1、首先判断当前skuId所在的商品有没有库存。如果有没。则直接返回false
        List<OrderItemVo> itemVos = lockVo.getItemVos();
        List<SkuIdHasStock> collect = itemVos.stream().map(item -> {
            SkuIdHasStock stock = new SkuIdHasStock();
            Long skuId = item.getSkuId();
            stock.setSkuId(skuId);
            List<Long> wareIds = this.baseMapper.selectBySkuId(skuId);
            if (wareIds == null || wareIds.size() == 0) {
                throw new NoStockException(skuId);
            } else {
                stock.setWareId(wareIds);
                stock.setCount(item.getCount());
                return stock;
            }
        }).collect(Collectors.toList());
        //查询库存，并锁定
        for (SkuIdHasStock hasStock : collect) {
            boolean locked=false;
            Long skuId = hasStock.getSkuId();
            List<Long> wareIds = hasStock.getWareId();
            for (Long wareId : wareIds) {
                Long num=this.baseMapper.lockStock(skuId,wareId,hasStock.getCount());
                    if (num==1){
                        //说明当前仓库有库存，可以锁住
                        //如果可以锁住，那么就在`wms_ware_order_task_detail` 记录以下锁定的工作细节。
                        StockLockedTo stockLockedTo= new StockLockedTo();
                            stockLockedTo.setOrderTaskID(wareOrderTaskEntity.getId());

                        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity(null,skuId
                        ,"",hasStock.getCount(),wareOrderTaskEntity.getId(),wareId,1);

                        detailService.save(detailEntity);
                        StockDetailTo stockDetailTo = new StockDetailTo();
                        BeanUtils.copyProperties(detailEntity, stockDetailTo);
                        stockLockedTo.setStockDetailTo(stockDetailTo);

                        // TODO 向rabbitmq发送消息     发送是往延迟队列里发送消息。锁定库存
                        rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", stockLockedTo);

                        locked=true;
                        break;
                    }else{
                        //说明锁失败了。 遍历下一个仓库。
                    }
            }
            if (locked==false){
                //说明某个商品 在所有仓库里遍历都库存不足。
                throw new NoStockException(skuId);
            }
        }
        //  如果前面都可以执行， 那么肯定是锁成功了
        return true;
    }

    @Override
    public void unlockStockM(StockLockedTo to) {

        StockDetailTo stockDetailTo = to.getStockDetailTo();
        Long orderTaskID = to.getOrderTaskID();
        if (orderTaskID!=null){
            //不为null  才说明库存锁定了
            WareOrderTaskEntity byId = orderTaskService.getById(orderTaskID);
            String orderSn = byId.getOrderSn();
            //通过订单号获取订单。
            R r = orderFeignService.getOrder(orderSn);
            if (r.getCode()==0){
                OrderVo data = r.getData(new TypeReference<OrderVo>() {
                });
                if (data==null||data.getStatus()== OrderStatusEnum.CANCLED.getCode()){
                    //如果订单为null 或者订单状态已经关闭
                    //订单数据回滚。 获取最新的订单任务详情。因为可能已经修改过了。
                    WareOrderTaskDetailEntity byId1 = detailService.getById(stockDetailTo.getId());
                    if (byId1.getLockStatus()==1){
                        unlockStock(stockDetailTo.getSkuId(),stockDetailTo.getWareId(),stockDetailTo.getSkuNum(),stockDetailTo.getId());
                    }
                }
            }else {
                throw  new RuntimeException("远程服务调用失败");
            }
        }
    }

    /**
     * 假设网络原因 30分钟后订单状态没有改变，
     * 当前为监听消息 去修改库存
     * @param vo
     */
    @Override
    public void unlockStockM(OrderTo vo) {
        //订单号
        String orderSn = vo.getOrderSn();
        //根据订单号去找到OrderTaskID
       WareOrderTaskEntity orderTaskEntity= orderTaskService.getByOrderSn(orderSn);
        Long id = orderTaskEntity.getId();
        //根据taskId去查询状态不为2的
        List<WareOrderTaskDetailEntity> detailEntities = detailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", id).eq("lock_status", 1));
        // 修改订单状态
        for (WareOrderTaskDetailEntity entity : detailEntities) {
            unlockStock(entity.getSkuId(),entity.getWareId(),entity.getSkuNum(),entity.getId());
        }
    }


    @Data
    class SkuIdHasStock{
        Long SkuId;
        List<Long> wareId;
        Integer count;
    }

}